﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using AFCEPF.BankApplication.BLL;
using AFCEPF.BankApplication.DAL;
using NUnit.Framework;

namespace AFCEPF.BankApplication.Tests.Conformance
{
    [TestFixture]
    class Test_MappedObjectsConformance
    {

        /// <summary>
        /// Sur chacune des classes mappées de la BLL,
        /// on teste qu'il n'existe pas deux propriétés
        /// décorées avec un FieldAttribute pour lequel
        /// la propriété IsPrimaryKey = true..
        /// </summary>
        [Test]
        public void Test_MappedClasses_ForPrimaryKeyUnicity()
        {
            bool result = true;

            // On récupère l'assembly BLL..
            Assembly assembly = typeof(Bank).Assembly;

            // pour chacunes des classes définies dans 
            // notre assembly..
            foreach (Type type in assembly.GetTypes())
            {
                // Si la classe (le type) en cours
                // dérive bien de DataMapper..
                if (isDerivedFrom<DataMapper>(type))
                {
                    // On récupère toutes ses propriétés
                    // mappées (décorées avec un FieldAttribute)..
                    var query = from n in type.GetProperties()
                                where ReflectionHelper.IsDecoratedWith<FieldAttribute>(n)
                                select n;

                    int counter = 0;
                    
                    // et l'on s'assure que le nombre de 
                    // propriétés pour lesquelles IsPrimaryKey
                    // est true est toujours 1
                    foreach (var item in query)
                    {                        
                        FieldAttribute attribute = ReflectionHelper.GetUniqueAttribute<FieldAttribute>(item);
                        if (attribute.IsPrimaryKey)
                        {
                            counter++;
                        }
                    }
                    result = (counter == 1);
                    if (!result)
                        break;
                }
            }

            Assert.IsTrue(result);
        }

        /// <summary>
        /// Test qu'il n'existe  pas dans notre BLL des classes 
        /// n'héritant pas de DataMapper, mais dont certaines 
        /// des propriétés sont quand même décorées avec
        /// un FieldAttribute.
        /// </summary>
        [Test]
        public void Test_NonMappedClasss_WithMappedFields()
        {
            bool result = true;

            // recupérer un type de la couche BLL..
            // récuperer l'assembly contenant ce type..
            Assembly assembly = typeof(Bank).Assembly;

            // pour chaque type ds notre assembly qui 
            // n'hérite pas de DataMapper..
            var query = from n in assembly.GetTypes()
                        where (!isDerivedFrom<DataMapper>(n))
                        select n;

            foreach (var type in query)
            {
                // On obtient la liste de ses propriétés décorées
                // (toujours 0 dans un cas normal)..
                var decoratedProperties = from p in type.GetProperties()
                                          //where ReflectionHelper.IsDecoratedWith<FieldAttribute>(p)
                                          where p.IsDecoratedWith<FieldAttribute>()
                                          select p;

                if (decoratedProperties.Count() > 0)
                {
                    //Console.WriteLine(type.Name);
                    result = false;
                    break;
                }
            }
            Assert.IsTrue(result);
            
        }
        
        /// <summary>
        /// Test qu'il n'existe  pas dans notre BLL des classes 
        /// n'héritant pas de DataMapper, mais quand même
        /// décorées avec un TableAttribute.
        /// </summary>
        [Test]
        public void Test_NonMappedClasss_WithTableAttribute()
        {
            bool result;

            // recupérer un type de la couche BLL..
            // récuperer l'assembly contenant ce type..
            Assembly assembly = typeof(Bank).Assembly;

            // requête :  chaque type ds notre assembly qui 
            // n'hérite pas de DataMapper mais qui est 
            // décoré avec un TableAttribute.
            var query = from n in assembly.GetTypes()
                        where (!isDerivedFrom<DataMapper>(n)) &&
                        //ReflectionHelper.IsDecoratedWith<TableAttribute>(n)
                        n.IsDecoratedWith<TableAttribute>()
                        select n;

            // on s'assure qu'il n'en existe pas.
            result = (query.Count() == 0);
            Assert.IsTrue(result);

        }


        /// <summary>
        /// Test qu'il n'existe  pas dans notre BLL des classes 
        /// héritant de DataMapper, mais dont deux propriétés
        /// mappent vers le même nom de champ.
        /// </summary>
        [Test]
        public void Test_MappedClasss_WithDuplicateFieldNameAttribute()
        {
            bool result = true;

            // On récupère tous les types héritant de DataMapper
            // contenus dans notre assembly BLL..
            var query = from t in typeof(Bank).Assembly.GetTypes()
                        where isDerivedFrom<DataMapper>(t)
                        select t;

            foreach (Type mappedType in query)
            {
                // On récupère un IEnumerable<FieldAttribute> représentant
                // tous les attributs FieldAttributes définis dans le type 
                // en cours..
                var fieldAttributes = from p in mappedType.GetProperties()
                                      where p.IsDecoratedWith<FieldAttribute>()
                                      select p.GetUniqueAttribute<FieldAttribute>();


                // on récupère un IEnumerable<string> contenant les noms 
                // des champs dupliqués dans les attributs Field pour le type
                // en cours (nombre devant être égal à zéro !!).
                var duplicateFieldNames = from attr in fieldAttributes
                                          group attr by attr.FieldName into fieldnames
                                          where fieldnames.Count() > 1
                                          select new { FieldName = fieldnames.Key };

                if (duplicateFieldNames.Count() > 0)
                {
                    result = false;
                    break;
                }
            }
            Assert.IsTrue(result);
        }

        /// <summary>
        /// Determines whether the specified Type is derived 
        /// from the type of T.
        /// </summary>
        /// <typeparam name="T">The Type</typeparam>
        /// <param name="typeToTest">The type to test.</param>
        /// <returns>
        /// true if the type to test is derived from T,
        /// else, false.
        /// </returns>        
        private bool isDerivedFrom<T>(Type typeToTest)
        {
            bool result = false;

            if (!typeToTest.IsInterface)
            {
                while (typeToTest.BaseType != typeof(object))
                {
                    if (typeToTest.BaseType == typeof(T))
                    {
                        result = true;
                        break;
                    }
                    typeToTest = typeToTest.BaseType;
                }
            }
            return result;
        }

    }
}
